﻿//Copyright (c) 2013 Tilo Szepan, Immo Wache
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
//documentation files (the "Software"), to deal in the Software without restriction, including without limitation
//the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

//The above copyright notice and this permission notice shall be included in all copies or substantial portions 
//of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
//THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
//CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace DuinoClient
{
    public class FSReader
    {
        public const string ServerPath = "http://192.168.2.44:8221/";
#if OS_WINDOWS
        public static string[] DefaultPathes = { "C:\\", Environment.GetFolderPath(Environment.SpecialFolder.Personal) + '\\', ServerPath };
        static string appdir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\DuinoExplorer\\";
        static string tmpDir = appdir + "tmpDir\\";
#else
        public static string[] DefaultPathes = { "/", Environment.GetFolderPath(Environment.SpecialFolder.Personal) + '/', ServerPath };
        static string appdir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/DuinoFinder/";
        static string tmpDir = appdir + "tmpDir/";
#endif
        static string historypath = appdir + "history.txt";

        public Action<Exception> Exceptions;
        public Action<StringCollection> SetClipboardFileList;
        public Action<object> Result;
        public Action<int> IsRemote;
        public Action DoStep { set { client.StepPerformed = value; } }

        HttpClient client;
        Stack<string> url_list = new Stack<string>();

        public FSReader()
        {
            client = new HttpClient();
            client.Exceptions = EmitException;
            client.Result = EmitResult;
        }

        public bool IsRemotePath(string path)
        {
            return path != null && path.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase);
        }
        public string LastUrl()
        {
            if (url_list.Count < 2) return null;
            url_list.Pop(); // this is the currently shown
            return url_list.Peek();
        }
        public void GetDirInfo(string path)
        {
            if (url_list.Count == 0 || url_list.Peek() != path)
            {
                url_list.Push(path);
            }
            path = ConvertSpecialPathes(path);
            bool isRem = IsRemotePath(path);
            if (isRem && IsRemote != null) IsRemote(-1);
            try
            {
                if (isRem)
                {
                    client.DirList(path);
                }
                else
                {
                    var d = new DirectoryInfo(path);
                    var fs = d.GetFileSystemInfos();
                    EmitResult(Convert(fs));
                }
            }
            catch (Exception ex)
            {
                EmitException(ex);
            }
        }
        public void Cancel()
        {
            client.CancelAsync();
        }

        public void DoCopy(StringCollection files)
        {
            if (files == null || files.Count == 0) return;
            if (IsRemotePath(files[0]))
            {
                if (IsRemote != null) IsRemote(files.Count + 1);
                ClearTmpDir();
                client.Result = (l) =>
                {
                    var put = FullDirInfo(tmpDir);
                    if (put != null && SetClipboardFileList != null)
                        SetClipboardFileList(put);

                    client.Result = EmitResult;
                    EmitResult(null);
                };
                client.DownloadItems(files, tmpDir);
            }
            else
            {
                if (SetClipboardFileList != null) SetClipboardFileList(files);
            }
        }
        public void DoPaste(string path, StringCollection files)
        {
            if (IsRemotePath(path))
            {
                if (IsRemote != null) IsRemote(files.Count + 1);
                if (!String.IsNullOrEmpty(path) && path.Last() != '/') path += '/';

                client.UploadItems(files, path);
            }
            else
            {
                if (!String.IsNullOrEmpty(path) && path.Last() != Path.DirectorySeparatorChar) path += Path.DirectorySeparatorChar;
                var res = new List<FileSystemItem>(files.Count);
                foreach (var f in files)
                {
                    try
                    {
                        var fi = new FileInfo(f);
                        if (fi.Exists)
                        {
                            fi.CopyTo(path + fi.Name);
                            res.Add(FileSystemItem.FromFSInfo(fi));
                        }
                        else
                        {
                            var fd = new DirectoryInfo(f);
                            if (fd.Exists)
                            {
                                CoppyFolderTo(fd, path);
                                res.Add(FileSystemItem.FromFSInfo(fd));
                            }
                            else
                            {
                                Console.WriteLine("not found: " + f);
                                throw new FileNotFoundException("Not found", f);
                            }
                        }
                    }
                    catch (Exception exc)
                    {
                        EmitException(exc);
                    }
                }
                EmitResult(res);
            }
        }
        public void DoDelete(StringCollection files)
        {
            if (files == null || files.Count == 0) return;
            if (IsRemotePath(files[0]))
            {
                if (IsRemote != null) IsRemote(-1);
                client.DeleteItems(files);
            }
            else
            {
                foreach (var itm in files)
                {
                    try
                    {
                        if (Directory.Exists(itm)) Directory.Delete(itm, true);
                        else File.Delete(itm);
                    }
                    catch (Exception ex)
                    {
                        EmitException(ex);
                    }
                }
                EmitResult(files);
            }
        }
        public void InsertNewFolder(string name)
        {
            try
            {
                if (IsRemotePath(name))
                {
                    if (IsRemote != null) IsRemote(-1);
                    client.CreateFolder(name);
                }
                else
                {
                    Directory.CreateDirectory(name);
                    EmitResult(name);
                }
            }
            catch (Exception ex)
            {
                EmitException(ex);
            }
        }
        public void Rename(string fullName, string newName)
        {
            try
            {
                if (IsRemotePath(fullName))
                {
                    if (IsRemote != null) IsRemote(-1);
                    client.RenameItem(fullName, newName);
                }
                else
                {
                    if (File.Exists(fullName))
                    {
                        var fi = new FileInfo(fullName);
                        fi.MoveTo(Path.Combine(fi.DirectoryName, newName));
                    }
                    else // if (Directory.Exists(fullName))
                    {
                        var di = new DirectoryInfo(fullName);
                        var par = di.Parent;
                        di.MoveTo(Path.Combine(par.FullName, newName));
                    }
                    EmitResult(newName);
                }
            }
            catch (Exception ex)
            {
                EmitException(ex);
            }
        }
        public string GetParentDir(string path)
        {
            if (!String.IsNullOrEmpty(path))
            {
                if (!IsRemotePath(path))
                {
                    var d = new DirectoryInfo(path);
                    var p = d.Parent;
                    return p != null ? p.FullName : null;
                }
                else
                {
                    int idx = path.TrimEnd('/').LastIndexOf('/');
                    return idx > 6 // "http://".Length
                        ? path.Substring(0, idx) : null;
                }
            }
            return null;
        }

        void CoppyFolderTo(DirectoryInfo folder, string path)
        {
            path = Path.Combine(path, folder.Name);
            try
            {
                if (!Directory.Exists(path))
                    Directory.CreateDirectory(path);
                var files = folder.GetFiles();
                foreach (var f in files)
                {
                    f.CopyTo(Path.Combine(path, f.Name), true);
                }
                var dirs = folder.GetDirectories();
                foreach (var d in dirs)
                {
                    CoppyFolderTo(d, path);
                }
            }
            catch (Exception exc)
            {
                EmitException(exc);
            }
        }

        List<FileSystemItem> Convert(FileSystemInfo[] fs)
        {
            //           if (fs == null || fs.Length == 0) return null;
            var res = new List<FileSystemItem>(fs.Length);
            foreach (var f in fs)
                res.Add(FileSystemItem.FromFSInfo(f));
            return res;
        }
        StringCollection FullDirInfo(string path)
        {
            var di = new DirectoryInfo(path);
            var fs = di.GetFileSystemInfos();
            if (fs.Length == 0) return null;
            var res = new StringCollection();
            foreach (var f in fs)
                res.Add(f.FullName);

            return res;
        }
        void ClearTmpDir()
        {
            var di = new DirectoryInfo(tmpDir);
            if (!di.Exists) di.Create();
            else
            {
                var items = di.GetFileSystemInfos();
                foreach (var itm in items)
                    if (itm is DirectoryInfo)
                    {
                        (itm as DirectoryInfo).Delete(true);
                    }
                    else
                        itm.Delete();
            }
        }
        public void SaveHistory(string[] items)
        {
            try
            {
                if (!Directory.Exists(appdir)) Directory.CreateDirectory(appdir);
                var strm = File.CreateText(historypath);
                if (items != null)
                {
                    foreach (var itm in items)
                    {
                        if (!String.IsNullOrEmpty(itm))
                            strm.WriteLine(itm);
                    }
                }
                strm.Close();
            }
            catch (Exception ex)
            {
                EmitException(ex);
            }
        }
        public string[] ReadHistory()
        {
            try
            {
                if (File.Exists(historypath))
                {
                    var res = File.ReadAllLines(historypath);
                    return res.Length > 0 ? res : new[] { Environment.GetFolderPath(Environment.SpecialFolder.Personal) };
                }
                else return DefaultPathes;
            }
            catch (Exception ex)
            {
                EmitException(ex);
            }
            return DefaultPathes;
        }
        string ConvertSpecialPathes(string path)
        {
            if (path.Equals("home", StringComparison.CurrentCultureIgnoreCase))
                return Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            return path;
        }
        public void EmitResult(object res) {
            if (Result != null) Result(res);
        }
        public void EmitException(Exception exc)
        {
            EmitResult(null);
            if (Exceptions != null) Exceptions(exc);
        }
    }
    public class FileSystemItem
    {
        public bool IsDir { get; private set; }
        public string Name { get; private set; }
        public string Size { get; private set; }
        public string Date { get; private set; }
        public FileSystemItem(string name, bool isDir, string date, string size)
        {
            Name = name; IsDir = isDir; Date = date; Size = size;
        }
        public static FileSystemItem Directory(string name, string date)
        {
            return new FileSystemItem(name, true, date, DirSize);
        }
        public static FileSystemItem File(string name, string date, string size)
        {
            return new FileSystemItem(name, false, date, size);
        }
        public static FileSystemItem FromFSInfo(FileSystemInfo info)
        {
            if (info is DirectoryInfo) return Directory(info.Name, info.LastWriteTime.ToString());
            else return File(info.Name, info.LastWriteTime.ToString(), (info as FileInfo).Length + " B");
        }
        public static string DirSize = "--";
    }
}
